package common;

import inferenceEngine.labels.Label;
import inferenceEngine.types.LinguisticValue;
import inferenceEngine.types.LinguisticVariable;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;

import process.Defuzzification;

/**
 * Class represent laptops.
 * It contains the labels the laptop has and supports computing recommended degree
 * @author Boshi Li
 *
 */
public class Laptop {
	private String name = "";
	private String brand = "";
	private String CPU = "";
	private int RAM = 0;
	private String GPU = "";
	private int graphicalMemory = 0;
	private double graphicalMemoryDegree = 0;
	private int HDDSize = 0;
	private double HDDSizeDegree = 0;
	private int SDDSize = 0;
	private double SDDSizeDegree = 0;
	private double weight = 0;
	private double weightDegree = 0;
	private double thickness = 0;
	private double thicknessDegree = 0;
	private double screenSize = 0;
	private double screenSizeDegree = 0;
	private double batteryLife = 0;
	private double batteryLifeDegree = 0;
	private double price = 0;
	private int CPUMark = 0;
	private double CPUMarkDegree = 0;
	private int GPUMark = 0;
	private double GPUMarkDegree = 0;
	private LinkedHashSet<String> list = new LinkedHashSet<String>();
	
	private ArrayList<Label> labels = new  ArrayList<Label>();
	private HashMap<String, LinguisticVariable > hashmap = null;
	private double RAMDegree;
	
	public Laptop( HashMap<String, LinguisticVariable > hashmap ){
		this.hashmap = hashmap;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getBrand() {
		return brand;
	}
	public void setBrand(String brand) {
		this.brand = brand;
	}
	public int getHDDSize() {
		return HDDSize;
	}
	public void setHDDSize(int hDDSize) {
		HDDSize = hDDSize;
	}
	public int getGraphicalMemory() {
		return graphicalMemory;
	}
	public void setGraphicalMemory(int graphicalMemory) {
		this.graphicalMemory = graphicalMemory;
	}
	public String getCPU() {
		return CPU;
	}
	public void setCPU(String cPU) {
		CPU = cPU;
	}
	public int getRAM() {
		return RAM;
	}
	public void setRAM(int rAM) {
		RAM = rAM;
	}
	public String getGPU() {
		return GPU;
	}
	public void setGPU(String gPU) {
		GPU = gPU;
	}
	public int getSDDSize() {
		return SDDSize;
	}
	public void setSDDSize(int sDDSize) {
		SDDSize = sDDSize;
	}
	public double getWeight() {
		return weight;
	}
	public void setWeight(double weight) {
		this.weight = weight;
	}
	public double getThickness() {
		return thickness;
	}
	public void setThickness(double thickness) {
		this.thickness = thickness;
	}
	public double getScreenSize() {
		return screenSize;
	}
	public void setScreenSize(double screenSize) {
		this.screenSize = screenSize;
	}
	public double getBatterySize() {
		return batteryLife;
	}
	public void setBatterySize(int batterySize) {
		this.batteryLife = batterySize;
	}
	public double getPrice() {
		return price;
	}
	public void setPrice(double price) {
		this.price = price;
	}
	
	@Override
	public int hashCode(){
		return name.hashCode();
	}
	
	@Override
	public boolean equals( Object o ){
		if( ! ( o instanceof Laptop ) ){
			return false;
		}
		Laptop target = (Laptop)o;
		if( this.name.equals( target.name ) 
	  && this.brand.equals( target.brand ) ){
			return true;
		}else{
			return false;
		}
	}
	
	public void setAttribute( String attributeName, String value ){
		switch( attributeName ){
		case "name":
			this.name = value;
			break;
		case "brand":
			this.brand = value;
			break;
		case "CPU":
			this.CPU = value;
			break;
		case "RAM":
			this.RAM = Integer.parseInt( value );
			break;
		case "GPU":
			this.GPU = value;
			break;
		case "graphicalMemory":
			this.graphicalMemory = Integer.parseInt( value );
			break;
		case "HDDSize":
			this.HDDSize = Integer.parseInt(value);
			break;
		case "SDDSize":
			this.SDDSize = Integer.parseInt(value);
			break;
		case "weight":
			this.weight = Double.parseDouble(value);
			break;
		case "thickness":
			this.thickness = Double.parseDouble(value);
			break;
		case "screenSize":
			this.screenSize = Double.parseDouble(value);
			break;
		case "batteryLife":
			this.batteryLife = Double.parseDouble(value);
			break;
		case "price":
			this.price = Double.parseDouble(value);
			break;
		case "CPUMark":
			this.CPUMark = Integer.parseInt(value);
			break;
		case "GPUMark":
			this.GPUMark = Integer.parseInt(value);
			break;
			
		}
	}
	public void addLabels(ArrayList<Label> labels) {
		this.labels.addAll( labels );
		
	}
	public void addLabel(Label label) {
		this.labels.add( label );
		
	}
	
	public ArrayList<Label> getLabels(){
		return labels;
	}
	
	public ArrayList<Label> getRecommendedLabels(){
		ArrayList<Label> recommended = new ArrayList<Label>();
		for( Label label : labels ){
			if( label.isRecommended() ){
				recommended.add( label );
			}
		}
		return recommended;
	}
	
	public double getPricePeformance(){
		return ( CPUMark + GPUMark*1 + RAM * 100 + graphicalMemory * 100 ) / price;
	}
	
	public double getRecommendMark(){
		double pricePeformance = getPricePeformance();
		double recommendedDegree = 1.0;
		for( Label l : labels ){
			if( l.isRecommended()&& l.getDegree()>=1.0 ){
				recommendedDegree*=l.getDegree();
			}
		}
		
		Defuzzification defuzzification = new Defuzzification();
		double value = defuzzification.defuzzy( getSuggestedDegree() );
		return pricePeformance * recommendedDegree * value ;
	}
	
	private double getSuggestedDegree(){
		double min = 1.0;
		LinguisticVariable var = hashmap.get("weight");
		if( min > getSuggestedDegree(var, weight) ){
			weightDegree = getSuggestedDegree(var, weight);
			min = getSuggestedDegree(var, weight);
		}
		var = hashmap.get("RAM");
		if( min > getSuggestedDegree(var, RAM) ){
			RAMDegree = getSuggestedDegree(var, RAM);
			min = getSuggestedDegree(var, RAM);
		}
		var = hashmap.get("video memory");
		if( min > getSuggestedDegree(var, graphicalMemory) ){
			graphicalMemoryDegree = getSuggestedDegree(var, graphicalMemory);
			min = getSuggestedDegree(var, graphicalMemory);
		}
		var = hashmap.get("disk size");
		if( min > getSuggestedDegree(var, HDDSize) ){
			HDDSizeDegree = getSuggestedDegree(var, HDDSize);
			min = getSuggestedDegree(var, HDDSize);
		}
		var = hashmap.get("thickness");
		if( min > getSuggestedDegree(var, thickness) ){
			thicknessDegree = getSuggestedDegree(var, thickness);
			min = getSuggestedDegree(var, thickness);
		}
		var = hashmap.get("battery life");
		if( min > getSuggestedDegree(var, batteryLife) ){
			batteryLifeDegree = getSuggestedDegree(var, batteryLife);
			min = getSuggestedDegree(var, batteryLife);
		}
		var = hashmap.get("CPU");
		if( min > getSuggestedDegree(var, CPUMark) ){
			CPUMarkDegree = getSuggestedDegree(var, CPUMark);
			min = getSuggestedDegree(var, CPUMark);
		}
		var = hashmap.get("GPU");
		if( min > getSuggestedDegree(var, GPUMark) ){
			GPUMarkDegree = getSuggestedDegree(var, GPUMark);
			min = getSuggestedDegree(var, GPUMark);
		}
		var = hashmap.get("screen size");
		if( min > getSuggestedDegree(var, screenSize) ){
			screenSizeDegree = getSuggestedDegree(var, screenSize);
			min = getSuggestedDegree(var, screenSize);
		}
		list.add(String.format("%s : %f", "suggested degree" , min ) );
		return min;
	}
	
	public ArrayList<String> getFuzzyLogicProcess(){
		return new ArrayList<String>(list);
	}
	
	/**
	 * should solve recommended and required conflict and 'min' function [ need to be implemented ]
	 * @param var
	 * @param x
	 * @return
	 */
	private double getSuggestedDegree( LinguisticVariable var, double x ){
		ArrayList<Double> requiredArray = new ArrayList<Double>();
		ArrayList<Double> recommendedArray = new ArrayList<Double>();
		double minRequired = 1;
		double minRecommended = 1;
		for( LinguisticValue value : var.getLinguisticValues() ){
			if( value.isRequired() ){
				double degree = value.getFuzzyValue( x );
				list.add( String.format("[ Required ] %s is %s : %f", var.getName(), value.getName(), degree  ));
//				return degree;
				requiredArray.add(degree);
			}else if( value.isRecommended() ){
				double degree = value.getFuzzyValue( x );
				list.add( String.format("[ Recommended ] %s is %s : %f", var.getName(), value.getName(), degree  ));
//				return degree;
				recommendedArray.add(degree);
			}
		}
		
		for( Double d : requiredArray ){
			if(minRequired>d){
				minRequired = d;
			}
		}
		
		for( Double d : recommendedArray ){
			if(minRecommended>d){
				minRecommended = d;
			}
		}
		
		return requiredArray.size() >0 ? minRequired : minRecommended ;
	}

	public void tooHeavy() {
		LinguisticVariable var = hashmap.get("weight");
		for( LinguisticValue value : var.getLinguisticValues() ){
			if( value.isRequired() ){
				value.reduceFuzzyValue( weight );
			}
		}
	}

	public void tooThick() {
		LinguisticVariable var = hashmap.get("thickness");
		for( LinguisticValue value : var.getLinguisticValues() ){
			if( value.isRequired() ){
				value.reduceFuzzyValue( thickness );
			}
		}
	}
	
}
